package com.example.service.impl;

import com.example.pojo.Product;
import com.example.service.ProductService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;

/**
 * @author yinyin
 * @create 2021/07/14 下午 3:36
 */
@Service
public class ProductServiceImpl implements ProductService {
    @Autowired
    private RestTemplate restTemplate;


    //------------------------------------服务熔断start----------------------------------------
    @HystrixCommand(commandProperties = {
            // 10s 内请求数大于 20 个就启动熔断器，当请求符合熔断条件触发 fallbackMethod 默认 20 个
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,
                    value = "10"),
            // 请求错误率大于 50% 就启动熔断器，然后 for 循环发起重试请求，当请求符合熔断条件触发 fallbackMethod
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,
                    value = "50"),
            // 熔断多少秒后去重试请求，默认 5s
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,
                    value = "5000"),
    }, fallbackMethod = "selectProductByIdFallback")
    @Override
    public Product selectProductById(Integer id) {

        System.out.println("-----selectProductById-----"
                + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME));
        // 模拟查询主键为 5 的商品信息会导致异常
        if (5 == id) {
            throw new RuntimeException("查询主键为 5 的商品信息导致异常");
        }
        return restTemplate.exchange(
                "http://product-service/product/listById/" + id,
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<Product>() {
                }
        ).getBody();
    }

    // 托底数据
    private Product selectProductByIdFallback(Integer id) {
        return new Product(id, "托底数据", 1, 2666D);
    }

    //---------------------------服务熔断end---------------------------
    //---------------------------信号量隔离start----------------------------------------------------
    @Override
    // 信号量隔离
    @HystrixCommand(commandProperties = {
            // 超时时间，默认 1000ms
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
                    value = "5000"),
            // 信号量隔离
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY,
                    value = "SEMAPHORE"),
            // 信号量最大并发，调小一些方便模拟高并发
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS,
                    value = "6")
    }, fallbackMethod = "selectProductListFallback")
    public List<Product> selectProductList() {
        System.out.println(Thread.currentThread().getName()+"orderService---selectProductList");
        return restTemplate.exchange(
                "http://product-service/product/list",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<Product>>() {
                }
        ).getBody();
    }
    //兜底方法
    public List<Product> selectProductListFallback(){
        //脏数据
        System.out.println("-----selectProductListFallback-----");
        return Arrays.asList(
                new Product(1, "托底数据-华为手机", 1, 5800D),
                new Product(2, "托底数据-联想笔记本", 1, 6888D),
                new Product(3, "托底数据-小米平板", 5, 2020D)
        );
    }
    //-----------------------------------------信号量隔离end----------------------------------------------------------
    //------------------------------------------线程隔离start------------------------------------------------------------------
   /* //@Cacheable(cacheNames = "orderService:product:list")
    @Override
    @HystrixCommand(groupKey = "order-productService-listPool",//服务名称,相同名称在同一个池中
            commandKey = "selectProductList",//接口名称,默认方法名
            threadPoolKey = "order-productService-listPool",//线程池名称,相同名称使用同一个池
            commandProperties = {
                    //超时时间,默认1000ms
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
                                     value="5000")
            },
            threadPoolProperties = {
                    // 线程池大小
                    @HystrixProperty(name = "coreSize", value = "6"),
                    // 队列等待阈值(最大队列长度，默认 -1)
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    // 线程存活时间，默认 1min
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    // 超出队列等待阈值执行拒绝策略
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "100")
            }, fallbackMethod = "selectProductListFallback"
    )

    public List<Product> selectProductList() {
        System.out.println(Thread.currentThread().getName()+"orderService---selectProductList");
        return restTemplate.exchange(
                "http://product-service/product/list",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<Product>>() {
                }
        ).getBody();
    }
    //兜底方法
    public List<Product> selectProductListFallback(){
        //脏数据
        System.out.println("-----selectProductListFallback-----");
        return Arrays.asList(
                new Product(1, "托底数据-华为手机", 1, 5800D),
                new Product(2, "托底数据-联想笔记本", 1, 6888D),
                new Product(3, "托底数据-小米平板", 5, 2020D)
        );
    }

    @HystrixCommand(groupKey = "order-productService-singlePool",// 服务名称，相同名称使用同一个线程池
            commandKey = "selectProductById",// 接口名称，默认为方法名
            threadPoolKey = "order-productService-singlePool",// 线程池名称，相同名称使用同一个线程池
            commandProperties = {
                    // 超时时间，默认 1000ms
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
                            value = "5000")
            },
            threadPoolProperties = {
                    // 线程池大小
                    @HystrixProperty(name = "coreSize", value = "3"),
                    // 队列等待阈值(最大队列长度，默认 -1)
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    // 线程存活时间，默认 1min
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    // 超出队列等待阈值执行拒绝策略
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "100")
            })
    @Override
    public Product selectProductById(Integer id) {
        System.out.println(Thread.currentThread().getName()+"orderService---selectProductById");
        return restTemplate.exchange(
                "http://product-service/product/listById/" + id,
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<Product>() {
                }
        ).getBody();
    }*/
   //-------------------------------------------线程隔离end--------------------------------------------
    @Override
    public List<Product> selectProductListByIds(List<Integer> ids) {
        //System.out.println("-----orderService---selectProductListByIds----");
        StringBuffer sb=new StringBuffer();
        ids.forEach(id->sb.append("id="+id+"&"));
        return restTemplate.exchange(
                "http://product-service/product/listByIds?"+sb.toString(),
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<Product>>() {
                }

        ).getBody();
    }




   /* @HystrixCollapser(batchMethod = "selectProductListByIds",//合并后要请求的方法
    scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,//全局生效
    collapserProperties = {
           //间隔多久的请求会进行合并   默认10ms
           @HystrixProperty(name = "timerDelayInMilliseconds",value = "20"),
            //批处理允许的最大请求数
           @HystrixProperty(name = "maxRequestsInBatch",value="200")
    }
    )
    @Override
    public Future<Product> selectProductById(Integer id) {
        System.out.println("-----orderService---selectProductListById----");
        return null;
     *//*   return restTemplate.exchange(
                "http://product-service/product/listById/" + id,
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<Product>() {
                }
        ).getBody();*//*

    }*/



}